import * as React from 'react'

import './interface'

// 参考资料 https://ts.xcatliu.com/

// 基本类型练习
export interface HelloProps {
  compiler: string
  framework: string
  isFunction?: boolean
  // 任意属性
  [propName: string]: any
  // 只读属性
  readonly id?: string
}

export const Hello = (props: HelloProps) => {
  React.useEffect(() => {
    // 数值
    let mynum: number = 123
    console.log(mynum)
    // mynum = "123";  // error

    // 数组
    let arr: Array<number> = [1, 2, 3]
    let arr1: number[] = [1, 2, 3]
    console.log(arr, arr1)
    // arr = "2123";  // error
    const xiaojiejies: { name: string; age: number }[] = [
      {
        name: 'ding',
        age: 24
      }
    ]
    type person = { name: string; age: number }
    const xiaojiejie2s: person[] = [
      {
        name: 'ding',
        age: 24
      }
    ]

    // 元组 Tuple
    let xx: [string, number] = ['hello', 10] // OK
    // Initialize it incorrectly
    // let xx1:  [string, number] = [10, "hello"]; // Error

    // 枚举     或者赋值: enum Color {Red = 1, Green = 2, Blue = 4}
    enum Color {
      Red,
      Green,
      Blue
    }
    let c: Color = Color.Green
    console.log(Color) // {0: "Red", 1: "Green", 2: "Blue", Red: 0, Green: 1, Blue: 2}
    /** 
     * 实现逻辑如下
     * var Color;
        (function (Color) {
            Color[Color["Red"] = 0] = "Red";
            Color[Color["Green"] = 1] = "Green";
            Color[Color["Blue"] = 2] = "Blue";
        })(Color || (Color = {}));
        var c = Color.Green;
     */

    //
    let temp, temp1
    console.log((temp = 10), (temp1 = 11)) // 10 11

    // 空 void
    // 某种程度上来说，void类型像是与any类型相反，它表示没有任何类型。 当一个函数没有返回值时，你通常会见到其返回值类型是void：
    function warnUser(): void {
      alert('This is my warning message')
    }

    // 声明一个void类型的变量没有什么大用，因为你只能为它赋予undefined和null：
    let myvoide: void = null

    // 在某处你想传入一个string或null或undefined，你可以使用联合类型string | null | undefined

    // 类型断言
    // 类型断言有两种形式。 其一是“尖括号”语法：
    let someValue: any = 'this is a string'
    // let strLength: number = (<string>someValue).length;
    // 另一个为as语法：
    let strLength: number = (someValue as string).length
    // 两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好；然而，当你在TypeScript里使用JSX时，只有as语法断言是被允许的。

    // 类型推论  如果没有明确的指定类型，那么 TypeScript 会依照类型推论（Type Inference）的规则推断出一个类型。
    let myFavoriteNumber = 'seven' // 等价于 let myFavoriteNumber: string = 'seven';
    // myFavoriteNumber = 7;  // error    // Type 'number' is not assignable to type 'string'.

    // 当然如果定义的时候没有赋值，不管之后有没有赋值，都会被推断成 any 类型而完全不被类型检查：
    // 变量如果在声明的时候，未指定其类型，那么它会被识别为任意值类型
    {
      let myFavoriteNumber // 等价于 let something: any;
      myFavoriteNumber = 'seven'
      myFavoriteNumber = 7
    }

    // 联合类型 使用 | 分隔每个类型
    {
      let myFavoriteNumber: string | number
      myFavoriteNumber = 'seven'
      myFavoriteNumber = 7
    }

    // 索引数组  NumberArray 表示：只要索引的类型是数字时，那么值的类型必须是数字。
    interface NumberArray {
      [index: number]: number
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5]

    // Cannot find name '$'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery`
    // 我们在my.d.ts里面声明全局变量 $ 就不会报错了
    console.log($('#root')[0])
    // $ = (msg) => alert(msg)
    // jQuery()
  }, [])

  return (
    <h1>
      Hello from {props.compiler} and {props.framework} and {props.isFunction.toString()}!
    </h1>
  )
}

function Typescript(props: any) {
  return (
    <React.Fragment>
      <Hello compiler="TypeScript" framework="React" isFunction={true} />
    </React.Fragment>
  )
}

export default Typescript
